feat(companion): add optimistic updates to availability edit screens#26931
Merged
dhairyashiil merged 8 commits intomainfrom Jan 16, 2026
Merged
feat(companion): add optimistic updates to availability edit screens#26931dhairyashiil merged 8 commits intomainfrom
dhairyashiil merged 8 commits intomainfrom
Conversation
- Refactor useUpdateSchedule hook with optimistic updates - Update EditAvailabilityNameScreen to use mutation hook instead of direct API call - Update EditAvailabilityNameScreen.ios.tsx with same pattern - Cache is updated immediately on save, then synced with server - On error, cache is rolled back to previous state
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…is empty - Update list cache in onMutate even when detail cache doesn't exist - Remove onSettled invalidation that was causing issues with staleTime: Infinity - Add fallback invalidation in onSuccess when list cache doesn't exist
…ates The previous optimistic updates implementation in EditAvailabilityNameScreen correctly updated the React Query cache, but AvailabilityDetailScreen was still using direct CalComAPIService.getScheduleById() calls with local useState, completely bypassing the cache. The disconnect: - EditAvailabilityNameScreen → useUpdateSchedule → Updates React Query Cache ✓ - AvailabilityDetailScreen → CalComAPIService.getScheduleById → Local state ✗ This meant when a user saved changes to a schedule's name/timezone, the cache was updated but the detail screen (and subsequently the list screen) never saw those updates because they weren't reading from the cache. Changes: - Refactor AvailabilityDetailScreen.tsx to use useScheduleById hook - Refactor AvailabilityDetailScreen.ios.tsx to use useScheduleById hook - Replace direct API calls with React Query for cache synchronization - Add RefreshControl for pull-to-refresh support - Use mutation hooks (useSetScheduleAsDefault, useDeleteSchedule) for actions - Derive availability/overrides data using useMemo from query result Now when EditAvailabilityNameScreen updates the cache, AvailabilityDetailScreen automatically reflects those changes because both read from the same cache.
…ion hook - Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook - Remove local isSaving state, use isPending from mutation hook instead - Cache is now updated automatically via the mutation hook's optimistic updates - Consistent pattern with EditAvailabilityNameScreen refactoring
- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook - Remove local isSaving state, use isPending from mutation hook instead - Cache is now updated automatically via the mutation hook's optimistic updates - Consistent pattern with EditAvailabilityNameScreen and EditAvailabilityOverrideScreen
…eduleById hook The working hours page (page 2 in the flow) was showing stale data because it used direct CalComAPIService.getScheduleById() calls with local useState, bypassing the React Query cache. The disconnect: - EditAvailabilityDayScreen → useUpdateSchedule → Updates React Query Cache ✓ - edit-availability-hours route → CalComAPIService.getScheduleById → Local state ✗ This meant when a user saved changes to a day's availability, the cache was updated but the working hours page never saw those updates because it wasn't reading from the cache. Changes: - Refactor edit-availability-hours.tsx to use useScheduleById hook - Refactor edit-availability-hours.ios.tsx to use useScheduleById hook - Replace direct API calls with React Query for cache synchronization - Now all 3 pages in the flow read from the same cache
Previously, when there were no date overrides, the Date Overrides section was just a plain View without any press handler, making it impossible for users to navigate to the edit override page to add new overrides. This follows the same pattern as other sections (Weekly Schedule, Timezone) which are always clickable regardless of their content state. Changes: - Wrap the 'No Overrides' section in AppPressable with navigation handler - Add chevron-forward icon to indicate it's tappable - Apply fix to both AvailabilityDetailScreen.tsx and .ios.tsx
Contributor
There was a problem hiding this comment.
4 issues found across 11 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="companion/components/screens/AvailabilityDetailScreen.ios.tsx">
<violation number="1" location="companion/components/screens/AvailabilityDetailScreen.ios.tsx:203">
P1: `useMemo` should not be used for side effects. This should be `useEffect` since calling `onActionsReady` is a side effect that notifies the parent component. The original code correctly used `useEffect` here.</violation>
<violation number="2" location="companion/components/screens/AvailabilityDetailScreen.ios.tsx:216">
P1: Side effects (`showErrorAlert` and `router.back()`) should not be called directly during render. This will execute on every render when error is truthy. Move this to a `useEffect` that depends on `error`.</violation>
</file>
<file name="companion/components/screens/AvailabilityDetailScreen.tsx">
<violation number="1" location="companion/components/screens/AvailabilityDetailScreen.tsx:203">
P1: `useMemo` should not be used for side effects. This callback invocation should use `useEffect` instead. `useMemo` is for memoizing computed values, and React may skip its execution or run it multiple times unpredictably.</violation>
<violation number="2" location="companion/components/screens/AvailabilityDetailScreen.tsx:216">
P1: Side effects (`showErrorAlert`, `router.back()`) should not be called during render. This will cause multiple alerts and navigation calls on re-renders. Move error handling to a `useEffect` that watches the `error` state.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Contributor
Devin AI is addressing Cubic AI's review feedbackNew feedback has been sent to the existing Devin session. ✅ Pushed commit |
- Change useMemo to useEffect for onActionsReady callback (side effect) - Move error handling (showErrorAlert, router.back) to useEffect - Keep early return for error state after useEffect hooks Fixes Cubic AI review feedback (confidence 9/10)
6 tasks
PeerRich
approved these changes
Jan 16, 2026
7 tasks
devin-ai-integration bot
added a commit
that referenced
this pull request
Jan 17, 2026
This adds a new companion-typecheck.yml workflow that runs TypeScript type checking for the companion app. This would have caught the missing useEffect import issue in PR #26931. Changes: - Add new companion-typecheck.yml workflow file - Update pr.yml to call the new workflow when companion files change - Add typecheck-companion to the required jobs list Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
anikdhabal
added a commit
that referenced
this pull request
Jan 19, 2026
* ci(companion): add separate typecheck workflow to catch type errors This adds a new companion-typecheck.yml workflow that runs TypeScript type checking for the companion app. This would have caught the missing useEffect import issue in PR #26931. Changes: - Add new companion-typecheck.yml workflow file - Update pr.yml to call the new workflow when companion files change - Add typecheck-companion to the required jobs list Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * add lint checks * test * remove * update typecheck * address review --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
anikdhabal
added a commit
that referenced
this pull request
Jan 19, 2026
* ci(companion): add separate typecheck workflow to catch type errors This adds a new companion-typecheck.yml workflow that runs TypeScript type checking for the companion app. This would have caught the missing useEffect import issue in PR #26931. Changes: - Add new companion-typecheck.yml workflow file - Update pr.yml to call the new workflow when companion files change - Add typecheck-companion to the required jobs list Co-Authored-By: anik@cal.com <adhabal2002@gmail.com> * add lint checks * test * remove * update typecheck * address review * chore:- hide apps with missing required keys from app store * update * Delete packages/app-store/_utils/hasRequiredAppKeys.test.ts * Update validateAppKeys.ts * Update validateAppKeys.ts * Update validateAppKeys.test.ts --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This was referenced Jan 20, 2026
This was referenced Jan 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Screen.Recording.2026-01-16.at.6.56.47.PM.mov
Fixes #26908
Fixes CAL-7077
What does this PR do?
Implements optimistic updates for availability edit screens in the companion app (iOS, Android, browser extension). This is part of a larger refactoring effort to add auto-refresh/reload after save actions.
Phase 1 - EditAvailabilityNameScreen:
useUpdateSchedulehook with full optimistic update support (onMutate, onSuccess, onError)EditAvailabilityNameScreen.tsxandEditAvailabilityNameScreen.ios.tsxto use the mutation hook instead of direct API callsPhase 2 - EditAvailabilityOverrideScreen:
EditAvailabilityOverrideScreen.tsxandEditAvailabilityOverrideScreen.ios.tsxto useuseUpdateSchedulemutation hookCalComAPIService.updateSchedulecalls with the mutation hookisSavingstate in favor ofisPendingfrom the mutation hookPhase 3 - EditAvailabilityDayScreen:
EditAvailabilityDayScreen.tsxandEditAvailabilityDayScreen.ios.tsxto useuseUpdateSchedulemutation hookCalComAPIService.updateSchedulecalls with the mutation hookisSavingstate in favor ofisPendingfrom the mutation hookSupporting Changes (by @dhairyashiil):
AvailabilityDetailScreen.tsxandAvailabilityDetailScreen.ios.tsxto useuseScheduleByIdhook instead of direct API callsRefreshControlfor pull-to-refresh supportuseSetScheduleAsDefaultanduseDeleteSchedulehooksUpdates since last revision
Fixed stale data issue on the Working Hours page (page 2 in the edit flow):
edit-availability-hours.tsxandedit-availability-hours.ios.tsxroute files to useuseScheduleByIdhookCalComAPIService.getScheduleById()calls with localuseState, bypassing the React Query cacheFixed Date Overrides section not clickable when empty:
AppPressablewith navigation handler and chevron iconMandatory Tasks (DO NOT REMOVE)
How should this be tested?
Phase 1 - Edit Name/Timezone:
Phase 2 - Edit Overrides:
Phase 3 - Edit Day Availability:
Date Overrides Empty State:
Error case: If network fails, UI should roll back to previous state and show error alert
Checklist
Human Review Checklist
AvailabilityDetailScreenproperly reads from React Query cache viauseScheduleById(critical for cache updates to propagate)edit-availability-hoursroute files properly read from React Query cache viauseScheduleByIduseUpdateSchedulehandlesavailabilityfield correctly via spread operatoronErrorrestores both detail and list caches correctlyLink to Devin run: https://app.devin.ai/sessions/0e35f5cdab8943dabb66fc182e4241fe
Requested by: Dhairyashil Shinde (@dhairyashiil)